import sys
import random
from dataclasses import dataclass
from PySide6 import QtCore, QtGui, QtWidgets
import os


def clamp(v, lo, hi):
    return max(lo, min(hi, v))


@dataclass
class Config:
    range_min: int = 1
    range_max: int = 10
    update_interval_ms: int = 300
    blink_interval_ms: int = 300
    enable_blink: bool = True
    blink_color_name: str = "red"
    font_family: str = "Segoe UI"
    font_size: int = 72
    text_color_name: str = "black"
    background_color_name: str = "transparent"


class DraggableLabel(QtWidgets.QLabel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._dragging = False
        self._drag_start = QtCore.QPoint()
        self.setAttribute(QtCore.Qt.WidgetAttribute.WA_TransparentForMouseEvents, False)
        self.setMouseTracking(True)

    def mousePressEvent(self, event: QtGui.QMouseEvent):
        if event.button() == QtCore.Qt.MouseButton.LeftButton:
            self._dragging = True
            self._drag_start = event.pos()
            self.setCursor(QtCore.Qt.CursorShape.ClosedHandCursor)
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event: QtGui.QMouseEvent):
        if self._dragging:
            parent = self.parentWidget()
            if parent:
                delta = event.pos() - self._drag_start
                new_pos = self.pos() + delta
                # 约束在父容器内
                new_x = clamp(new_pos.x(), 0, max(0, parent.width() - self.width()))
                new_y = clamp(new_pos.y(), 0, max(0, parent.height() - self.height()))
                self.move(new_x, new_y)
        super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event: QtGui.QMouseEvent):
        self._dragging = False
        self.setCursor(QtCore.Qt.CursorShape.ArrowCursor)
        super().mouseReleaseEvent(event)


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("随机数助手 - Python 版")
        self.resize(1000, 600)

        self.rng = random.Random()
        self.cfg = Config()
        self._current_number = 0
        self._blink_on = False
        self._normal_color = QtGui.QColor(self.cfg.text_color_name)
        self._running = False

        # 定时器
        self.update_timer = QtCore.QTimer(self)
        self.update_timer.timeout.connect(self.on_update_tick)
        self.update_timer.setInterval(self.cfg.update_interval_ms)

        self.blink_timer = QtCore.QTimer(self)
        self.blink_timer.timeout.connect(self.on_blink_tick)
        self.blink_timer.setInterval(self.cfg.blink_interval_ms)

        # 主布局
        central = QtWidgets.QWidget(self)
        self.setCentralWidget(central)
        hbox = QtWidgets.QHBoxLayout(central)

        # 左侧显示区域
        self.display_frame = QtWidgets.QFrame()
        self.display_frame.setFrameShape(QtWidgets.QFrame.Shape.StyledPanel)
        self.display_frame.setStyleSheet(f"background-color: {self.cfg.background_color_name};")
        self.display_frame.setMinimumSize(360, 300)

        display_layout = QtWidgets.QVBoxLayout(self.display_frame)
        display_layout.setContentsMargins(8, 8, 8, 8)
        display_layout.setSpacing(0)

        self.number_label = DraggableLabel("0", self.display_frame)
        self.number_label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.number_label.setStyleSheet(f"color: {self.cfg.text_color_name};")
        font = QtGui.QFont(self.cfg.font_family, self.cfg.font_size)
        font.setWeight(QtGui.QFont.Weight.Medium)
        self.number_label.setFont(font)
        self.number_label.setFixedSize(320, 160)
        # 初始居中
        self.number_label.move(
            max(0, self.display_frame.width() // 2 - self.number_label.width() // 2),
            max(0, self.display_frame.height() // 2 - self.number_label.height() // 2),
        )

        display_layout.addWidget(self.number_label, alignment=QtCore.Qt.AlignmentFlag.AlignCenter)

        # 右侧控制面板
        right_scroll = QtWidgets.QScrollArea()
        right_scroll.setWidgetResizable(True)
        right_panel = QtWidgets.QWidget()
        right_scroll.setWidget(right_panel)
        right_layout = QtWidgets.QVBoxLayout(right_panel)
        self._right_scroll = right_scroll
        self._right_panel = right_panel

        # 参数设置 - 随机数更新
        grp_update = QtWidgets.QGroupBox("随机数更新")
        upd_layout = QtWidgets.QVBoxLayout(grp_update)

        line1 = QtWidgets.QHBoxLayout()
        line1.addWidget(QtWidgets.QLabel("最小值"))
        self.ed_min = QtWidgets.QLineEdit(str(self.cfg.range_min))
        self.ed_min.setFixedWidth(100)
        line1.addWidget(self.ed_min)
        line1.addSpacing(8)
        line1.addWidget(QtWidgets.QLabel("最大值"))
        self.ed_max = QtWidgets.QLineEdit(str(self.cfg.range_max))
        self.ed_max.setFixedWidth(100)
        line1.addWidget(self.ed_max)

        line2 = QtWidgets.QHBoxLayout()
        line2.addWidget(QtWidgets.QLabel("更新间隔(ms)"))
        self.ed_update_ms = QtWidgets.QLineEdit(str(self.cfg.update_interval_ms))
        self.ed_update_ms.setFixedWidth(100)
        line2.addWidget(self.ed_update_ms)

        line3 = QtWidgets.QHBoxLayout()
        line3.addWidget(QtWidgets.QLabel("闪烁间隔(ms)"))
        self.ed_blink_ms = QtWidgets.QLineEdit(str(self.cfg.blink_interval_ms))
        self.ed_blink_ms.setFixedWidth(100)
        line3.addWidget(self.ed_blink_ms)
        self.chk_blink = QtWidgets.QCheckBox("启用闪烁")
        self.chk_blink.setChecked(self.cfg.enable_blink)
        line3.addWidget(self.chk_blink)

        line4 = QtWidgets.QHBoxLayout()
        line4.addWidget(QtWidgets.QLabel("闪烁颜色"))
        self.cmb_blink_color = QtWidgets.QComboBox()
        for name in ["red", "orange", "yellow", "green", "blue", "purple"]:
            self.cmb_blink_color.addItem(name)
        self.cmb_blink_color.setCurrentText(self.cfg.blink_color_name)
        line4.addWidget(self.cmb_blink_color)

        line5 = QtWidgets.QHBoxLayout()
        self.btn_toggle = QtWidgets.QPushButton("开始")
        self.update_toggle_button_ui()
        line5.addWidget(self.btn_toggle)

        for ln in (line1, line2, line3, line4, line5):
            upd_layout.addLayout(ln)

        # 显示样式
        grp_style = QtWidgets.QGroupBox("显示样式")
        sty_layout = QtWidgets.QVBoxLayout(grp_style)

        line6 = QtWidgets.QHBoxLayout()
        line6.addWidget(QtWidgets.QLabel("字体"))
        self.cmb_font = QtWidgets.QComboBox()
        db = QtGui.QFontDatabase()
        families = db.families()
        # 简化：添加常见字体优先，再附加部分系统字体
        preferred = ["Segoe UI", "Microsoft YaHei", "Arial", "Consolas", "Calibri"]
        seen = set()
        for fam in preferred + families[:50]:
            if fam not in seen:
                self.cmb_font.addItem(fam)
                seen.add(fam)
        idx = self.cmb_font.findText(self.cfg.font_family)
        self.cmb_font.setCurrentIndex(idx if idx >= 0 else 0)
        line6.addWidget(self.cmb_font)

        line7 = QtWidgets.QHBoxLayout()
        line7.addWidget(QtWidgets.QLabel("字号"))
        self.sld_size = QtWidgets.QSlider(QtCore.Qt.Orientation.Horizontal)
        self.sld_size.setRange(12, 120)
        self.sld_size.setValue(self.cfg.font_size)
        self.sld_size.setFixedWidth(200)
        line7.addWidget(self.sld_size)

        line8 = QtWidgets.QHBoxLayout()
        line8.addWidget(QtWidgets.QLabel("文本颜色"))
        self.cmb_text_color = QtWidgets.QComboBox()
        for name in ["black", "white", "darkblue", "darkred", "darkgreen"]:
            self.cmb_text_color.addItem(name)
        self.cmb_text_color.setCurrentText(self.cfg.text_color_name)
        line8.addWidget(self.cmb_text_color)

        line9 = QtWidgets.QHBoxLayout()
        line9.addWidget(QtWidgets.QLabel("背景颜色"))
        self.cmb_bg_color = QtWidgets.QComboBox()
        for name in ["transparent", "white", "lightgray", "lightyellow", "lightblue"]:
            self.cmb_bg_color.addItem(name)
        self.cmb_bg_color.setCurrentText(self.cfg.background_color_name)
        line9.addWidget(self.cmb_bg_color)

        tip = QtWidgets.QLabel("提示：按住数字区域可拖动位置")
        tip.setStyleSheet("color: gray;")

        for ln in (line6, line7, line8, line9):
            sty_layout.addLayout(ln)
        sty_layout.addWidget(tip)

        # 智能随机决策
        grp_decision = QtWidgets.QGroupBox("智能随机决策")
        dec_layout = QtWidgets.QVBoxLayout(grp_decision)
        dec_layout.addWidget(QtWidgets.QLabel("设置触发阈值（在范围内）与测试次数"))

        line10 = QtWidgets.QHBoxLayout()
        line10.addWidget(QtWidgets.QLabel("动作数量"))
        self.cmb_actions = QtWidgets.QComboBox()
        self.cmb_actions.addItems(["2", "3"])
        self.cmb_actions.setCurrentIndex(0)
        line10.addWidget(self.cmb_actions)
        line10.addSpacing(8)
        line10.addWidget(QtWidgets.QLabel("测试次数"))
        self.ed_runs = QtWidgets.QLineEdit("100")
        self.ed_runs.setFixedWidth(100)
        line10.addWidget(self.ed_runs)

        line11 = QtWidgets.QHBoxLayout()
        self.lbl_th_a_title = QtWidgets.QLabel("弃牌阈值≤")
        line11.addWidget(self.lbl_th_a_title)
        self.ed_th_a = QtWidgets.QLineEdit("3")
        self.ed_th_a.setFixedWidth(100)
        line11.addWidget(self.ed_th_a)

        line12 = QtWidgets.QHBoxLayout()
        self.lbl_th_b_title = QtWidgets.QLabel("下注33%阈值≤")
        line12.addWidget(self.lbl_th_b_title)
        self.ed_th_b = QtWidgets.QLineEdit("6")
        self.ed_th_b.setFixedWidth(100)
        line12.addWidget(self.ed_th_b)

        line13 = QtWidgets.QHBoxLayout()
        self.lbl_th_c_title = QtWidgets.QLabel("下注75%阈值≤")
        line13.addWidget(self.lbl_th_c_title)
        self.ed_th_c = QtWidgets.QLineEdit("8")
        self.ed_th_c.setFixedWidth(100)
        line13.addWidget(self.ed_th_c)

        # 动作名称自定义
        line14 = QtWidgets.QHBoxLayout()
        line14.addWidget(QtWidgets.QLabel("弃牌名称"))
        self.ed_name_a = QtWidgets.QLineEdit("弃牌")
        self.ed_name_a.setFixedWidth(140)
        line14.addWidget(self.ed_name_a)

        line15 = QtWidgets.QHBoxLayout()
        line15.addWidget(QtWidgets.QLabel("下注33%名称"))
        self.ed_name_b = QtWidgets.QLineEdit("下注33%")
        self.ed_name_b.setFixedWidth(140)
        line15.addWidget(self.ed_name_b)

        line16 = QtWidgets.QHBoxLayout()
        line16.addWidget(QtWidgets.QLabel("下注75%名称"))
        self.ed_name_c = QtWidgets.QLineEdit("下注75%")
        self.ed_name_c.setFixedWidth(140)
        line16.addWidget(self.ed_name_c)

        # 列出每次随机结果
        self.chk_list_each = QtWidgets.QCheckBox("列出每次随机结果")
        self.chk_list_each.setChecked(True)

        self.btn_run_tests = QtWidgets.QPushButton("运行测试并分析")
        self.txt_result = QtWidgets.QPlainTextEdit()
        self.txt_result.setReadOnly(True)
        self.txt_result.setFixedHeight(180)

        # 区间提示标签
        self.lbl_segments = QtWidgets.QLabel("")
        self.lbl_segments.setStyleSheet("color: gray;")

        for ln in (line10, line11, line12, line13, line14, line15, line16):
            dec_layout.addLayout(ln)
        dec_layout.addWidget(self.lbl_segments)
        dec_layout.addWidget(self.chk_list_each)
        dec_layout.addWidget(self.btn_run_tests)
        dec_layout.addWidget(QtWidgets.QLabel("结果"))
        dec_layout.addWidget(self.txt_result)

        # 右侧添加组与折叠按钮（默认隐藏“显示样式”）
        right_layout.addWidget(grp_update)
        self.btn_style_toggle = QtWidgets.QPushButton("▶ 显示样式")
        self.btn_style_toggle.setFlat(True)
        self.btn_style_toggle.setStyleSheet("text-align: left; color: #0066cc;")
        right_layout.addWidget(self.btn_style_toggle)
        self.grp_style = grp_style
        self.grp_style.setVisible(False)
        right_layout.addWidget(self.grp_style)

        # 新增：独立决策模块页签（2/3/4动作）
        grp_modules = QtWidgets.QGroupBox("独立决策模块")
        mod_layout = QtWidgets.QVBoxLayout(grp_modules)
        self.modules_tabs = QtWidgets.QTabWidget()
        self.mod2_controls = self.build_decision_tab(actions_count=2)
        self.modules_tabs.addTab(self.mod2_controls['container'], "2动作")
        self.mod3_controls = self.build_decision_tab(actions_count=3)
        self.modules_tabs.addTab(self.mod3_controls['container'], "3动作")
        self.mod4_controls = self.build_decision_tab(actions_count=4)
        self.modules_tabs.addTab(self.mod4_controls['container'], "4动作")
        mod_layout.addWidget(self.modules_tabs)
        right_layout.addWidget(grp_modules)
        right_layout.addStretch(1)

        # 汇总到主布局
        hbox.addWidget(self.display_frame, stretch=2)
        hbox.addWidget(right_scroll, stretch=3)

        # 信号连接
        self.btn_toggle.clicked.connect(self.on_toggle_clicked)
        self.cmb_blink_color.currentTextChanged.connect(self.on_colors_changed)
        self.cmb_text_color.currentTextChanged.connect(self.on_colors_changed)
        self.cmb_bg_color.currentTextChanged.connect(self.on_colors_changed)
        self.cmb_font.currentTextChanged.connect(self.on_font_changed)
        self.sld_size.valueChanged.connect(self.on_font_changed)
        self.chk_blink.toggled.connect(self.on_blink_toggled)
        self.btn_run_tests.clicked.connect(self.on_run_tests)
        self.btn_style_toggle.clicked.connect(self.on_style_toggle_clicked)
        # 联动更新标签
        self.ed_min.textChanged.connect(self.update_decision_labels)
        self.ed_max.textChanged.connect(self.update_decision_labels)
        self.ed_th_a.textChanged.connect(self.update_decision_labels)
        self.ed_th_b.textChanged.connect(self.update_decision_labels)
        self.ed_th_c.textChanged.connect(self.update_decision_labels)
        self.cmb_actions.currentTextChanged.connect(self.update_decision_labels)
        self.ed_name_a.textChanged.connect(self.update_decision_labels)
        self.ed_name_b.textChanged.connect(self.update_decision_labels)
        self.ed_name_c.textChanged.connect(self.update_decision_labels)

        # 初始渲染
        self.update_display_styles()
        self.update_decision_labels()
        self.set_initial_height_no_scroll()

    def on_start(self):
        self.cfg.range_min = self.read_int(self.ed_min, self.cfg.range_min)
        self.cfg.range_max = self.read_int(self.ed_max, self.cfg.range_max)
        if self.cfg.range_min > self.cfg.range_max:
            self.cfg.range_min, self.cfg.range_max = self.cfg.range_max, self.cfg.range_min

        self.cfg.update_interval_ms = clamp(self.read_int(self.ed_update_ms, self.cfg.update_interval_ms), 10, 100000)
        self.cfg.blink_interval_ms = clamp(self.read_int(self.ed_blink_ms, self.cfg.blink_interval_ms), 10, 100000)
        self.update_timer.setInterval(self.cfg.update_interval_ms)
        self.blink_timer.setInterval(self.cfg.blink_interval_ms)

        self.update_timer.start()
        self.blink_timer.start()

    def on_stop(self):
        self.update_timer.stop()
        self.blink_timer.stop()

    def on_update_tick(self):
        self._current_number = self.rng.randint(self.cfg.range_min, self.cfg.range_max)
        self.number_label.setText(str(self._current_number))

    def on_blink_tick(self):
        if not self.chk_blink.isChecked():
            self.number_label.setStyleSheet(f"color: {self.cfg.text_color_name};")
            return
        self._blink_on = not self._blink_on
        blink_color = self.cmb_blink_color.currentText()
        color_to_use = blink_color if self._blink_on else self.cmb_text_color.currentText()
        self.number_label.setStyleSheet(f"color: {color_to_use};")

    def on_colors_changed(self):
        self.cfg.text_color_name = self.cmb_text_color.currentText()
        self.cfg.background_color_name = self.cmb_bg_color.currentText()
        self.cfg.blink_color_name = self.cmb_blink_color.currentText()
        self.update_display_styles()

    def on_font_changed(self):
        self.cfg.font_family = self.cmb_font.currentText()
        self.cfg.font_size = self.sld_size.value()
        self.update_display_styles()

    def on_blink_toggled(self, checked: bool):
        self.cfg.enable_blink = checked
        if not checked:
            self.number_label.setStyleSheet(f"color: {self.cfg.text_color_name};")

    def update_display_styles(self):
        font = QtGui.QFont(self.cfg.font_family, self.cfg.font_size)
        font.setWeight(QtGui.QFont.Weight.Medium)
        self.number_label.setFont(font)
        self.number_label.setStyleSheet(f"color: {self.cfg.text_color_name};")
        self.display_frame.setStyleSheet(f"background-color: {self.cfg.background_color_name};")

    # 从同目录文本读取默认动作名称（每行一个，忽略空行与#注释）
    def load_action_names(self):
        names = []
        try:
            base_dir = os.path.dirname(__file__)
            file_path = os.path.join(base_dir, 'action_names.txt')
            with open(file_path, 'r', encoding='utf-8') as f:
                for line in f:
                    s = line.strip()
                    if s and not s.startswith('#'):
                        names.append(s)
        except Exception:
            # 文件不存在或读取失败时返回空列表，走回退默认
            pass
        return names

    # 读取整数的安全方法，缺省回退到默认值
    def read_int(self, line_edit: QtWidgets.QLineEdit, default_value: int) -> int:
        try:
            return int(line_edit.text())
        except Exception:
            return default_value

    def on_toggle_clicked(self):
        if not self._running:
            self.on_start()
            self._running = True
        else:
            self.on_stop()
            self._running = False
        self.update_toggle_button_ui()

    def update_toggle_button_ui(self):
        if self._running:
            self.btn_toggle.setText("停止")
            self.btn_toggle.setStyleSheet("QPushButton { background-color: #d9534f; color: white; }")
        else:
            self.btn_toggle.setText("开始")
            self.btn_toggle.setStyleSheet("QPushButton { background-color: #5cb85c; color: white; }")

    def on_run_tests(self):
        # 读取配置与阈值
        minv = self.read_int(self.ed_min, self.cfg.range_min)
        maxv = self.read_int(self.ed_max, self.cfg.range_max)
        if minv > maxv:
            minv, maxv = maxv, minv
        runs = clamp(self.read_int(self.ed_runs, 100), 1, 10_000_000)
        actions = 2 if self.cmb_actions.currentText() == "2" else 3
        th_a = self.read_int(self.ed_th_a, 3)
        th_b = self.read_int(self.ed_th_b, 6)
        th_c = self.read_int(self.ed_th_c, 8)
        name_a = self.ed_name_a.text().strip() or "弃牌"
        name_b = self.ed_name_b.text().strip() or "下注33%"
        name_c = self.ed_name_c.text().strip() or "下注75%"
        list_each = self.chk_list_each.isChecked()

        # 阈值约束到范围内并升序，确保区间不重叠
        th_a = clamp(th_a, minv, maxv)
        th_b = clamp(th_b, minv, maxv)
        th_c = clamp(th_c, minv, maxv)
        if actions == 2:
            th_a, th_b = sorted([th_a, th_b])
        else:
            th_a, th_b, th_c = sorted([th_a, th_b, th_c])

        a = b = c = other = 0
        per_lines = []
        for i in range(runs):
            val = self.rng.randint(minv, maxv)
            if val <= th_a:
                a += 1
                act = name_a
            elif val <= th_b and actions >= 2:
                b += 1
                act = name_b
            elif val <= th_c and actions >= 3:
                c += 1
                act = name_c
            else:
                other += 1
                act = "未触发"
            if list_each:
                per_lines.append(f"{i+1}: 值 {val} -> {act}")

        lines = []
        lines.append(f"范围: [{minv}, {maxv}], 测试次数: {runs}, 动作数: {actions}")
        lines.append(f"区间定义：{name_a}=[{minv}, {th_a}], "
                     + (f"{name_b}=({th_a}, {th_b}]" if actions >= 2 else "")
                     + (f", {name_c}=({th_b}, {th_c}]" if actions >= 3 else ""))
        lines.append(f"{name_a} 触发: {a} 次，占比 {a * 100.0 / runs:.2f}%")
        if actions >= 2:
            lines.append(f"{name_b} 触发: {b} 次，占比 {b * 100.0 / runs:.2f}%")
        if actions >= 3:
            lines.append(f"{name_c} 触发: {c} 次，占比 {c * 100.0 / runs:.2f}%")
        lines.append(f"未触发任何动作: {other} 次，占比 {other * 100.0 / runs:.2f}%")

        total_span = maxv - minv + 1
        def segment_span(lo_inclusive: int, hi_inclusive: int) -> int:
            lo = max(lo_inclusive, minv)
            hi = min(hi_inclusive, maxv)
            return max(0, hi - lo + 1)

        seg_a = segment_span(minv, th_a)
        seg_b = segment_span(th_a + 1, th_b) if actions >= 2 else 0
        seg_c = segment_span(th_b + 1, th_c) if actions >= 3 else 0

        target_a = seg_a / total_span
        target_b = seg_b / total_span if actions >= 2 else 0
        target_c = seg_c / total_span if actions >= 3 else 0

        obs_a = a / runs
        obs_b = b / runs if runs else 0
        obs_c = c / runs if runs else 0

        lines.append("—— 决策有效性粗评 ——")
        lines.append(f"{name_a}: 目标≈{target_a:.2%}, 实际≈{obs_a:.2%}")
        if actions >= 2:
            lines.append(f"{name_b}: 目标≈{target_b:.2%}, 实际≈{obs_b:.2%}")
        if actions >= 3:
            lines.append(f"{name_c}: 目标≈{target_c:.2%}, 实际≈{obs_c:.2%}")

        # 决策结果高亮
        lines.append("—— 决策结果 ——")
        candidates = [(obs_a, name_a)]
        if actions >= 2:
            candidates.append((obs_b, name_b))
        if actions >= 3:
            candidates.append((obs_c, name_c))
        other_rate = other / runs if runs else 0.0
        candidates.append((other_rate, "未触发"))
        max_val = max(v for v, _ in candidates)
        winners = [n for v, n in candidates if abs(v - max_val) < 1e-9]
        target_map = {name_a: target_a}
        if actions >= 2:
            target_map[name_b] = target_b
        if actions >= 3:
            target_map[name_c] = target_c
        if "未触发" in winners and len(winners) == 1:
            lines.append(f"【结论】未触发占比最高（实际≈{max_val:.2%}）。建议调整阈值或增加动作数量。")
        else:
            names_display = "、".join([n for n in winners])
            lines.append(f"【结论】优先选择：{names_display}（实际≈{max_val:.2%}）")
            if len(winners) == 1 and winners[0] in target_map:
                tgt = target_map[winners[0]]
                diff = abs(max_val - tgt)
                lines.append(f"理由：该动作在样本中触发占比最高，目标≈{tgt:.2%}，与实际差≈{diff:.2%}。")

        if list_each:
            lines.append("—— 逐条结果 ——")
            # 为避免极端大输出导致卡顿，提示仅在大量数据时考虑取消勾选
            lines.extend(per_lines)

        self.txt_result.setPlainText("\n".join(lines))

    def update_decision_labels(self):
        # 读取并计算有效区间与名称
        try:
            minv = int(self.ed_min.text())
        except Exception:
            minv = self.cfg.range_min
        try:
            maxv = int(self.ed_max.text())
        except Exception:
            maxv = self.cfg.range_max
        if minv > maxv:
            minv, maxv = maxv, minv

        actions = 2 if self.cmb_actions.currentText() == "2" else 3
        def to_int_safe(le, d):
            try:
                return int(le.text())
            except Exception:
                return d

        th_a = clamp(to_int_safe(self.ed_th_a, 3), minv, maxv)
        th_b = clamp(to_int_safe(self.ed_th_b, 6), minv, maxv)
        th_c = clamp(to_int_safe(self.ed_th_c, 8), minv, maxv)
        if actions == 2:
            th_a, th_b = sorted([th_a, th_b])
        else:
            th_a, th_b, th_c = sorted([th_a, th_b, th_c])

        name_a = (self.ed_name_a.text().strip() or "弃牌")
        name_b = (self.ed_name_b.text().strip() or "下注33%")
        name_c = (self.ed_name_c.text().strip() or "下注75%")

        # 将校正后的阈值回写到输入框
        try:
            self.ed_th_a.blockSignals(True)
            self.ed_th_b.blockSignals(True)
            self.ed_th_c.blockSignals(True)
            self.ed_th_a.setText(str(th_a))
            self.ed_th_b.setText(str(th_b))
            self.ed_th_c.setText(str(th_c))
        finally:
            self.ed_th_a.blockSignals(False)
            self.ed_th_b.blockSignals(False)
            self.ed_th_c.blockSignals(False)

        # 更新阈值标题以反映当前动作名称与不等式链
        text_a = f"{name_a} 的阈值上界 ≤ {th_a}"
        if actions >= 2:
            text_a += f"  <  {name_b} 的阈值上界 ≤"
        self.lbl_th_a_title.setText(text_a)

        text_b = f"{name_b} 的阈值上界 ≤ {th_b}"
        if actions >= 3:
            text_b += f"  <  {name_c} 的阈值上界 ≤"
        self.lbl_th_b_title.setText(text_b)

        text_c = f"{name_c} 的阈值上界 ≤ {th_c}"
        self.lbl_th_c_title.setText(text_c)

        # 更新区间提示
        segs = [f"{name_a}=[{minv}, {th_a}]"]
        if actions >= 2:
            segs.append(f"{name_b}=({th_a}, {th_b}]")
        if actions >= 3:
            segs.append(f"{name_c}=({th_b}, {th_c}]")
        self.lbl_segments.setText("区间定义：" + ", ".join(segs))

    # 新增：构建独立决策模块页签
    def build_decision_tab(self, actions_count: int):
        w = QtWidgets.QWidget()
        lay = QtWidgets.QVBoxLayout(w)
        # 基本参数
        lineA = QtWidgets.QHBoxLayout()
        lineA.addWidget(QtWidgets.QLabel("最小值"))
        ed_min = QtWidgets.QLineEdit("1")
        ed_min.setFixedWidth(80)
        lineA.addWidget(ed_min)
        lineA.addSpacing(6)
        lineA.addWidget(QtWidgets.QLabel("最大值"))
        ed_max = QtWidgets.QLineEdit("10")
        ed_max.setFixedWidth(80)
        lineA.addWidget(ed_max)
        lineA.addSpacing(6)
        lineA.addWidget(QtWidgets.QLabel("测试次数"))
        ed_runs = QtWidgets.QLineEdit("100")
        ed_runs.setFixedWidth(90)
        lineA.addWidget(ed_runs)
        chk_each = QtWidgets.QCheckBox("列出逐条")
        chk_each.setChecked(False)
        lineA.addWidget(chk_each)
        lay.addLayout(lineA)
        # 阈值与名称
        thresholds = []
        names = []
        for i in range(actions_count - 1):
            h = QtWidgets.QHBoxLayout()
            h.addWidget(QtWidgets.QLabel(f"阈值t{i+1} ≤"))
            le = QtWidgets.QLineEdit(str(3 + i * 3))
            le.setFixedWidth(90)
            thresholds.append(le)
            h.addWidget(le)
            lay.addLayout(h)
        # 从文本文件读取默认动作名称
        default_names = self.load_action_names()
        for i in range(actions_count):
            h = QtWidgets.QHBoxLayout()
            h.addWidget(QtWidgets.QLabel(f"动作{chr(ord('A')+i)}名称"))
            # 优先使用文本文件的默认值，否则回退到“动作A/B/C/D”
            initial = default_names[i] if i < len(default_names) else f"动作{chr(ord('A')+i)}"
            le = QtWidgets.QLineEdit(initial)
            le.setFixedWidth(140)
            names.append(le)
            h.addWidget(le)
            lay.addLayout(h)
        # 区间定义标签（每个tab独立显示）
        seg_label = QtWidgets.QLabel("区间定义：")
        seg_label.setWordWrap(True)
        lay.addWidget(seg_label)
        # 运行与结果
        btn_run = QtWidgets.QPushButton("运行该模块")
        result = QtWidgets.QPlainTextEdit()
        result.setReadOnly(True)
        result.setFixedHeight(160)
        lay.addWidget(btn_run)
        lay.addWidget(QtWidgets.QLabel("结果"))
        lay.addWidget(result)
        controls = {
            'container': w,
            'min': ed_min,
            'max': ed_max,
            'runs': ed_runs,
            'each': chk_each,
            'thresholds': thresholds,
            'names': names,
            'segments_label': seg_label,
            'result': result,
        }
        btn_run.clicked.connect(lambda _=False, c=controls, k=actions_count: self.run_independent_module(k, c))
        return controls

    # 新增：运行独立决策模块逻辑（使用独立随机数）
    def run_independent_module(self, actions_count: int, c: dict):
        rng = random.Random()  # 独立随机数发生器
        def to_int_safe(le, d):
            try:
                return int(le.text())
            except Exception:
                return d
        minv = to_int_safe(c['min'], 1)
        maxv = to_int_safe(c['max'], 10)
        if minv > maxv:
            minv, maxv = maxv, minv
        runs = clamp(to_int_safe(c['runs'], 100), 1, 10_000_000)
        ths = [clamp(to_int_safe(le, 3 + i*3), minv, maxv) for i, le in enumerate(c['thresholds'])]
        ths.sort()
        names = [le.text().strip() or f"动作{chr(ord('A')+i)}" for i, le in enumerate(c['names'])]
        list_each = c['each'].isChecked()
        # 计数
        counts = [0 for _ in range(actions_count)]
        per_lines = []
        for i in range(runs):
            val = rng.randint(minv, maxv)
            idx = None
            for j in range(actions_count - 1):
                if val <= ths[j]:
                    idx = j
                    break
            if idx is None:
                idx = actions_count - 1
            counts[idx] += 1
            act_name = names[idx]
            if list_each:
                per_lines.append(f"{i+1}: 值 {val} -> {act_name}")
        # 文本输出
        lines = []
        lines.append(f"范围: [{minv}, {maxv}], 测试次数: {runs}, 动作数: {actions_count}")
        # 区间定义
        segs = []
        def seg_span(lo, hi):
            lo = max(lo, minv)
            hi = min(hi, maxv)
            return max(0, hi - lo + 1)
        total_span = maxv - minv + 1
        last = minv
        for j in range(actions_count - 1):
            segs.append(f"{names[j]}=[{last}, {ths[j]}]")
            last = ths[j] + 1
        segs.append(f"{names[-1]}=[{last}, {maxv}]")
        # 生成并显示区间定义
        seg_text = "区间定义：" + ", ".join(segs)
        if 'segments_label' in c:
            c['segments_label'].setText(seg_text)
        lines.append(seg_text)
        # 触发统计
        ratios = [counts[i] * 100.0 / runs for i in range(actions_count)]
        for i in range(actions_count):
            lines.append(f"{names[i]} 触发: {counts[i]} 次，占比 {ratios[i]:.2f}%")
        # 目标比例
        targets = []
        last = minv
        for j in range(actions_count - 1):
            targets.append(seg_span(last, ths[j]) / total_span)
            last = ths[j] + 1
        targets.append(seg_span(last, maxv) / total_span)
        lines.append("—— 决策有效性粗评 ——")
        for i in range(actions_count):
            lines.append(f"{names[i]}: 目标≈{targets[i]:.2%}, 实际≈{counts[i]/runs:.2%}")
        # 结论
        lines.append("—— 决策结果 ——")
        max_rate = max(counts[i]/runs for i in range(actions_count))
        winners = [names[i] for i in range(actions_count) if abs(counts[i]/runs - max_rate) < 1e-9]
        if len(winners) == 1:
            winner = winners[0]
            idx = names.index(winner)
            diff = abs(counts[idx]/runs - targets[idx])
            lines.append(f"【结论】优先选择：{winner}（实际≈{max_rate:.2%}），与目标差≈{diff:.2%}")
        else:
            lines.append(f"【结论】并列最高：{'、'.join(winners)}（实际≈{max_rate:.2%}）")
        if list_each:
            lines.append("—— 逐条结果 ——")
            lines.extend(per_lines)
        c['result'].setPlainText("\n".join(lines))
        c['result'].moveCursor(QtGui.QTextCursor.End)
        c['result'].ensureCursorVisible()
        sb = c['result'].verticalScrollBar()
        if sb:
            sb.setValue(sb.maximum())

    def on_style_toggle_clicked(self):
        expanded = self.grp_style.isVisible()
        self.grp_style.setVisible(not expanded)
        self.btn_style_toggle.setText("▼ 显示样式" if not expanded else "▶ 显示样式")

    def set_initial_height_no_scroll(self):
        QtWidgets.QApplication.processEvents()
        right_h = self._right_panel.sizeHint().height()
        # 更紧凑的左侧基准高度
        left_h = max(self.display_frame.minimumSizeHint().height(), 280)
        target_h = max(left_h, right_h) + 16  # 边距缩小
        target_h = max(360, target_h)  # 启动下限再降一些
        self.resize(self.width(), int(target_h))
 

 
 
def main():
    # Ensure crisp rendering on high-DPI displays
    QtWidgets.QApplication.setAttribute(QtCore.Qt.ApplicationAttribute.AA_UseHighDpiPixmaps)
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec())


if __name__ == "__main__":
    main()